using System;
using System.Text;
using System.Data;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Web;

using Framework;

namespace SchemaMonitor
{
    //Collection Class (Customisable half)
    public partial class CTagList
    {
        #region Filters
        #endregion

        #region Aggregation
        public string Path
        {
            get
            {
                var names = new List<string>(this.Count);
                foreach (var i in this)
                    names.Add(i.TagName);
                return CUtilities.ListToString(names, ".");
            }
        }
        #endregion

        #region Searching (Optional)
        //Represents a simple search box to search PK and any string columns (add overloads as required, based on the pattern below)
        //e.g. public CTagList Search(string nameOrId, int forTypeId, int isTypeId, bool? isList, bool? isCustom) { ...
        public CTagList Search(string nameOrId)
        {
            //1. Normalisation
            nameOrId = (nameOrId??string.Empty).Trim().ToLower();
            
            //2. Start with a complete list
            CTagList results = this;
            
            //3. Use any available index, such as those generated for fk/bool columns
            //Normal Case - non-unique index (e.g. foreign key)
            //if (int.MinValue != forTypeId) results = results.GetByForTypeId(forTypeId);
            //if (int.MinValue != isTypeId) results = results.GetByIsTypeId(isTypeId);
            //if (isList.HasValue)   results = results.GetByIsList(isList.Value); //Customise bool filters according to UI (e.g. for checkbox, use simple bool and bias in one direction)
            //if (isCustom.HasValue)   results = results.GetByIsCustom(isCustom.Value); //Customise bool filters according to UI (e.g. for checkbox, use simple bool and bias in one direction)

            //Special case - unique index (e.g. primary key)
            /*
            if (!string.IsNullOrEmpty(nameOrId)) 
            {
                int id;
                if (int.TryParse(nameOrId, out id))
                {
                    CTag obj = this.GetById(id);
                    if (null != obj)
                    {
                        results = new CTagList(1);
                        results.Add(obj);
                        return results;
                    }
                }
            }
            */
            
            //4. Exit early if remaining (non-index) filters are blank
            if (string.IsNullOrEmpty(nameOrId)) return results; 
            
            //5. Manually search each record using custom match logic, building a shortlist
            CTagList shortList = new CTagList();
            foreach (CTag i in results)
                if (Match(nameOrId, i))
                    shortList.Add(i); 
            return shortList;
        }
        //Manual Searching e.g for string-based columns i.e. anything not indexed (add more params if required)
        private bool Match(string name, CTag obj)
        {
            if (!string.IsNullOrEmpty(name)) //Match any string column
            {
                if (null != obj.TagName && obj.TagName.ToLower().Contains(name))   return true;
                return false;   //If filter is active, reject any items that dont match
            }
            return true;    //No active filters (should catch this in step #4)
        }
        #endregion

        #region Cloning
        public CTagList Clone(CDataSrc target) //, int parentId)
        {
            //No Transaction
            if (target is CDataSrcRemote)
                return Clone(target, null); //, parentId);

            //Transaction
            using (IDbConnection cn = target.Local.Connection())
            {
                IDbTransaction tx = cn.BeginTransaction();
                try
                {
                    CTagList clone = Clone(target, tx); //, parentId);
                    tx.Commit();
                    return clone;
                }
                catch
                {
                    tx.Rollback();
                    throw;
                }
            }
        }
        public CTagList Clone(CDataSrc target, IDbTransaction txOrNull) //, int parentId)
        {
            CTagList list = new CTagList(this.Count);
            foreach (CTag i in this)
                list.Add(i.Clone(target, txOrNull)); //, parentId));  *Child entities must reference the new parent
            return list;
        }
        #endregion
        
        #region Export to Csv
        //Web - Need to add a project reference to System.Web, or comment out these two methods
        public void ExportToCsv(HttpResponse response) { ExportToCsv(response, "Tags.csv"); }
        public void ExportToCsv(HttpResponse response, string fileName)
        {
            CDataSrc.ExportToCsv(response, fileName); //Standard response headers
            StreamWriter sw = new StreamWriter(response.OutputStream);
            ExportToCsv(sw);
            sw.Flush();
            response.End();
        }

        //Non-web
        public void ExportToCsv(string filePath)
        {
            StreamWriter sw = new StreamWriter(filePath);
            ExportToCsv(sw);
            sw.Close();
        }

        //Logic
        protected void ExportToCsv(StreamWriter sw)
        {
            string[] headings = new string[] { "TagId", "TagForTypeId", "TagIsList", "TagName", "TagIsTypeId", "TagIsCustom", "TagListNesting" };
            CDataSrc.ExportToCsv(headings, sw);
            foreach (CTag i in this)
            {
                object[] data = new object[] { i.TagId, i.TagForTypeId, i.TagIsList, i.TagName, i.TagIsTypeId, i.TagIsCustom, i.TagListNesting };
                CDataSrc.ExportToCsv(data, sw);
            }
        }
        #endregion


        List<string> _names;
        public List<string> Names
        {
            get
            {
                if (null == _names || _names.Count != this.Count)
                {
                    var temp = new List<string>(this.Count);
                    foreach (CTag i in this)
                        temp.Add(i.Parent.IsSystem ? i.TagName : i.Path);
                    _names = temp;
                }
                return _names;
            }
        }

        public new void Add(CTag item)
        {
            if (null != _index && !_index.ContainsKey(item.TagId))
                _index[item.TagId] = item;

            _indexByForTypeId = null;
            _indexByIsCustom = null;
            _indexByIsList = null;
            _indexByIsTypeId = null;
            _indexByName = null;

            base.Add(item);
		}
		public new void Remove(CTag item)
		{
			if (null != _index && _index.ContainsKey(item.TagId))
				_index.Remove(item.TagId);

			_indexByForTypeId = null;
			_indexByIsCustom = null;
			_indexByIsList = null;
			_indexByIsTypeId = null;
			_indexByName = null;
			base.Remove(item);
		}


		//Matching Assist
		public bool FoundAll(Dictionary<string, object> dict)
        {
            return FoundAll(new List<string>(dict.Keys));
        }
        public bool FoundAll(List<string> tagNames)
        {
            if (tagNames.Count == 0)
                return false;

            foreach (var i in tagNames)
                if (null == GetByName(i))
                    return false;

            return true;
        }

        //Unique index on name (in context of children subset)
        public CTag GetByName(string key)
        {
            CTag temp = null;
            if (IndexByName.TryGetValue(key, out temp))
                return temp;
            return null;
        }
        private Dictionary<string, CTag> _indexByName;
        private Dictionary<string, CTag> IndexByName
        {
            get
            {
                if (null == _indexByName || _indexByName.Count > this.Count)
                {
                    var temp = new Dictionary<string, CTag>(this.Count);
                    foreach (var i in this)
                        temp.Add(i.TagName, i);
                    _indexByName = temp;
                }
                else if (_indexByName.Count < this.Count)
                {
                    foreach (var i in this)
                        if (!_indexByName.ContainsKey(i.TagName))
                            _indexByName.Add(i.TagName, i);
                }
                return _indexByName;
            }
        }
    }
}
